package cn.javasea.rbac.shiro.service.impl;

import cn.javasea.rbac.shiro.service.SysUserLoginService;
import cn.javasea.rbac.shiro.shiro.User;
import cn.javasea.rbac.shiro.util.DesEncryptUtil;
import cn.javasea.rbac.shiro.util.LoginInfor;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.util.Base64;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

/**
 * @Description: 账号登录接口实现类
 * @Author: xiangchao
 * @Date: 2023/2/1
 */
@Service
public class SysUserLoginServiceImpl implements SysUserLoginService {

    private static final Logger log = LoggerFactory.getLogger(SysUserLoginServiceImpl.class);

    private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

    private final String IRP_USER_LOGIN_SSO_TOKEN = "IRP_USER_LOGIN_SSO_TOKEN_";

    @Value("${sso.use}")
    private boolean ssoUse;

    @Value("${sso.serviceUrl}")
    private String serviceUrl;

    @Value("${privateKey}")
    private String privateKey;

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 登录实现方法
     * @author xiangchao
     * @date 2023/2/1 16:23
     * @param username 用户名
     * @param password 密码
     */
    @Override
    public JSONObject login(String username, String password, LoginInfor infor) {
//        infor.valid();
        //连续输错密码一定次数将冻结账户
//        int repeatNum = 5;  //允许密码连续错误次数
//        int retrySecond = 300;  //密码错误后该时间内再错误算连续错误
//        int lockSecond = 600;   //账户锁定时长（缓存过期时长）
//        Integer errorNumber = 0;
//        String userLoginKey = "";
//        String lockWords = "由于重试密码次数超出限制，账号" + infor.getUserName() + "被锁定"+ (lockSecond / 60)+ "分钟";
//        if(StringUtils.isNotEmpty(infor.getUserName())){
//            errorNumber = StringUtils.isNotEmpty(userLoginKey)? Integer.parseInt(userLoginKey): 0;
//            errorNumber++;
//            //达到一定次数，锁定用户一定时间，账户锁定时间取决于缓存过期时长
//            if(errorNumber > repeatNum){
//                return lockWords;
//            }
//        }
        User user = null;
        try {
//            String ps = RSAUtils.decode(infor.getPassword(), privateKey);
//            infor.setPassword(ps);
//
//            String ip = IPUtil.getUserIP(request);
//            if(StringUtils.isNotBlank(infor.getIp())){
//                ip = infor.getIp();
//            }
//            String loginResult = loginCheck(infor.getUserName(), infor.getPassword(),ip,"",infor.getMobile(),infor.getMobileCode(),infor.getToken());
//            if (StringUtils.isNotEmpty(loginResult)) {
//                if(StringUtils.isNotEmpty(infor.getUserName()) && "用户名或密码错误!".equals(loginResult)){
//                    //如密码错误将错误次数缓存到Redis，错误达到一定次数以上，缓存过期时间设置为10分钟，达到锁定10分钟效果
//                    loginResult = errorNumber >= repeatNum? lockWords: loginResult;
//                    jedisCluster.setex(userLoginKey, errorNumber >= repeatNum? lockSecond: retrySecond,errorNumber+ "");
//                }
//                throw new MsgException(loginResult);
//            }
            Subject currentUser = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            currentUser.login(token);
            // 构建缓存用户信息返回给前端
            user = (User) currentUser.getPrincipals().getPrimaryPrincipal();
            user.setPassword("admin123");
            user.setUsername("admin");
            user.setId(9L);
            user.setToken(currentUser.getSession().getId().toString());
            log.info("登录成功...............");
            //redisTemplate.opsForValue().set("shiro:userToken", currentUser.getSession().getId().toString());
            return JSONObject.parseObject(JSONObject.toJSONString(user));
        } catch (IncorrectCredentialsException e) {
            return null;
        } catch (Exception e) {
            log.error("登录异常: " + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 登录验证包含SSO单点登录
     * @author xiangchao
     * @date 2023/2/2 15:41
     */
    @Override
    public String loginCheck(String account, String pwd, String ip, String deviceId, String mobile, String mobileCode, String token) {
        String checkResult = null;
        //如果是测试环境地址，跳过登录验证
        if(!ssoUse || "RPA_205544".equals(account)){
            log.info("跳过SSO单点登录验证");
            return null;
        }
        try {
            log.info("SSO单点登录认证");
            String userTokenKey = IRP_USER_LOGIN_SSO_TOKEN + account;
            StringBuilder bf = new StringBuilder();
            if(StringUtils.isNotBlank(account)){
                bf.append("userName=").append(URLEncoder.encode(account, "UTF-8"));
            }
            if(StringUtils.isNotBlank(pwd)){
                bf.append("&password=").append(URLEncoder.encode(pwd, "UTF-8"));
            }
            if(StringUtils.isNotBlank(ip)){
                bf.append("&ip=").append(URLEncoder.encode(ip, "UTF-8"));
            }
            if(StringUtils.isNotBlank(deviceId)){
                bf.append("&deviceId=").append(URLEncoder.encode(deviceId, "UTF-8"));
            }
            if(StringUtils.isNotBlank(mobileCode)){
                bf.append("&mobileCode=").append(URLEncoder.encode(mobileCode, "UTF-8"));
                if(StringUtils.isNotBlank("jedisCluster.get(userTokenKey)")){
                    bf.append("&token=").append(URLEncoder.encode("jedisCluster.get(userTokenKey)", "UTF-8"));
                }
            }
            String data = bf.toString();
            PostMethod methodPost = new PostMethod(serviceUrl);
            methodPost.addParameter("appName", "OAS");
            methodPost.addParameter("type", "json");
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(data.getBytes(StandardCharsets.UTF_8));
            byte[] digestByte = md.digest();
            String digest = toString(digestByte);
            String base64Encoded = new String(Base64.encodeBase64(data.getBytes(StandardCharsets.UTF_8)));
            String dataAfterDESEncode = DesEncryptUtil.encryptToHex(base64Encoded.getBytes(StandardCharsets.UTF_8), "12345678");
            String finalData = digest + "&" + dataAfterDESEncode;
            methodPost.addParameter("data", finalData);

            HttpClient httpClient = new HttpClient();

            httpClient.executeMethod(methodPost);
            String response = new String(methodPost.getResponseBody(), StandardCharsets.UTF_8);
            String result="";
            // 拆分摘要和结果信息
            String[] digestAndResult = response.split("&");
            String resultMid = digestAndResult[1];

            // 解密响应结果
            //resultMid="oA3C5afnK/w=";
            String afterDESResult = DesEncryptUtil.decrypt(resultMid,"12345678");
            String afterBase64Decode = new String(Base64.decodeBase64(afterDESResult.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8);
            result = afterBase64Decode;

            //生成响应结果摘要信息
            MessageDigest sd = MessageDigest.getInstance("MD5");
            sd.update(afterBase64Decode.getBytes(StandardCharsets.UTF_8));

            JSONObject responseJson = JSONObject.parseObject(result);
            Object entryObject = responseJson.get("code");
            int i=(int)entryObject;

            if(i==0) {
                //查询账号是否存在，存在(修改)，不存在(添加)
//                User user = userServiceImpl.findByName(account);
//                if (user != null) {
//                    user.setSignOnPassword(pwd);
//                } else {
//                    user = new User();
//                    user.setAccount(account);
//                    user.setSignOnPassword(pwd);
//                }
//                //若登陆失败(密码或账号不正确)，则不删除redis中的token
//                if (userServiceImpl.checkAccount(account, pwd)) {
//                    jedisCluster.del(userTokenKey);
//                }
            }else if(i==63 || i==64){
                JSONObject dataJson = responseJson.getJSONObject("data");
                //获取返回的token
                String ssoToken = dataJson.getString("token");
                //获取返回的手机号
                String ssoMobile = dataJson.getString("mobile");
                //jedisCluster.setex(userTokenKey,1800,ssoToken);
                Object exception = responseJson.get("desc");
                checkResult = ssoMobile+","+exception.toString();
            }else{
                Object exception = responseJson.get("desc");
                checkResult = exception.toString();
                log.info("双因子验证响应结果:{}",responseJson);
            }

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            String errorResult  = "";
            if("1".equals(e.getMessage())){
                errorResult = "OA系统用户被禁用!";
            }else if("2".equals(e.getMessage())){
                errorResult = "您的密码不符合系统策略，系统要求重置密码才可以登录";
            }else if("4".equals(e.getMessage())){
                errorResult = "验证密码成功，密码复杂度够，但密码到期需更换(初始密码也为此提示)";
            }else if("5".equals(e.getMessage())){
                errorResult = "由于重试密码次数超出限制，账号"+account+"被暂时锁定";
            }else if("6".equals(e.getMessage())){
                errorResult = "用户名或密码错误!";
            }else if("7".equals(e.getMessage())){
                errorResult = "OA系统内部错误";
            }else {
                errorResult = "请求出现未知错误,请及时排查...";
            }
            return errorResult;
        }
        return checkResult;
    }

    public static String toString(byte[] data) {
        if (data == null) {
            return "null!";
        }
        int l = data.length;
        char[] out = new char[l << 1];
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS[0x0F & data[i]];
        }
        return new String(out);
    }
}
